拡張Kコンパイラ ――――――――――――――――――――――――――――――――――――――― FM−7/8活用研究 掲載 FM−7/8 拡張Kコンパイラ Kコンパイラ マシン語   ”K”   $2000−$41FF S=$2000 マシン語   ”RT”  $4D00−$4FFF ファイル版Kコンパイラ BASIC ”MAIN” マシン語  ”RTF”  $4D00−$4FFF マシン語  ”KF”   $2000−$41FF S=$2000 裏RAM版 リロケータ ソース    ”RELS” オブジェクト       ”REL”  $5000−$507F エグゼキューター ソース ”EXES” オブジェクト       ”EXE”  $5000−$503C リロケータ&エグゼキューター マシン語         ”EXE”  $7C00−$7C4F ――――――――――――――――――――――――――――――――――――――― FM−8活用研究で既に“拡張Kコンパイラ”を発表しましたが、 その後『FM−7用Kコンパイラはないか?』とか 『コンパイラをリロケータブルにできないか?』 との問い合わせが多くあったため、 ここで再び発表することにしました。 上述の活用研究ではKコンパイラの特徴、 展開されるオブジェクトなどについて述べましたが、 ある程度マシン語をかじった人や コンパイラに慣れている人でなければ 理解できなかったのではないかと思います。 ここでは、実際にどうやって使うかとか、 効率的なデバッグなどについて記してみたいと思います。 ・―――――――――――――――――・ |      概   要      | ・―――――――――――――――――・ 多くのコンパイラがそうであるように、 Kコンパイラもコンパイラ本休とランタイム・ルーチンを持っています。 コンパイラ本体は 実際にソース・プログラムからCPUが実行できる オブジェクトに変換するためのプログラムです。 このオブジェクトはそのまま実行できるわけではなく、 入出力(画面表示、キー入力など)や演算に関するものは 大部分がサブルーチン・コールの形(JSR $OOOO)に変換されます。 このサブルーチン群をランタイム・ルーチンと呼びます。 コンパイル後のオブジェクトは 一般にリロケータブル・バイナリと呼ばれ、 これにランタイムを付けて実行できるようになった オブジェクトをエグゼキュータブル・バイナリ またはロード・モジュールと呼びます。 大型計算機などのコンパイラは リロケータブル・バイナリから エグゼキュータブル・バイナリにするために リンカやリンケージ、エディタを備えています。 Kコンパイラはそれ自身で記述されたコンパイラなので、 コンパイルするときはランタイム・ルーチンが必要です。 また、リロケータブル・バイナリはその名のとおり リロケータブルなので、ランタイム・ルーチンさえあれば メモリ上のどこへ置いても実行できます。 ランタイム・ルーチンは オブジェクトから一定の番地をコールされるため、 リロケートはできません。 これをリロケータブルにするためには前述のような リンカやリンケージ・エディタが必要となります。 以上、コンパイラの慨要について述べてみましたが、 ちょっと難かしかったでしょうか? ただ、コンパイラについて述べるのが目的ではないし、 詳しく説明しようとすれば かなりの量になるのでここではこれぐらいにしておきます。 ・―――――――――――――――――・ |     実際の使い方      | ・―――――――――――――――――・ ■プログラムの作成 KコンパイラのプログラムはBASICプログラムと同じように作成します。 ただ、このときREM文 の形式(行番号の後に“’”を付ける)にしないと コンパイルできません。 また、プログラムを打ち込むときはコンパイラは必要ありません。 できあがったプログラムはすぐにコンパイルせず、 カセットやディスクにセーブしておいてください。 ■コンパイル コンパイルしたいプログラムを入れた状態で CLEAR10,&H1FFFとしてください。 ここで、“Out of memory” というエラーが出なければ そのままコンパイルできます。 リスト1、2のコンパイラとランタイム・ルーチンをロードし、 EXEC&H2000としてコンパイラを起動してください。 正常にコンパイルできれば、 オブジェクトの大きさを16進数で表示します。 ここで一時、次のようにしてセーブしてください。 ・――――――――――――――――――――――――――・ | SAVEM"ファイル名",&H4D00,&H5000+オブジェクトのサイズ,&H5000 | ・――――――――――――――――――――――――――・ 実行アドレスはオブジェクトの先頭アドレスです。 通常は5000番地ですが、オブジェクトはリロケータブルなので 他の領域に移すこともできます。 また、表1にもあるように207D,E番地の内容を変えれば 他の番地にもオブジェクトを作れます。 ただし、この番地の内容を変更するときは 図1のメモリ・マップをよく見て 他の領域と重ならないように注意してください。 さて、ここまではソース・プログラムが 1FFF番地を越えない場合のコンパイル方法ですが、 このままではのであまり大きなプログラムをコンパイルするためには、 FM−8ではファイル版への変更、 FM−7では裏RAMを使う方法があります。 図1 メモリ・マップ ・―――――――――――――・ |   ・―――――――・ | |   | ソース・プログラム | | | 2000|―――――――| | |   |■■■■■■■| | |   |■コンパイラ本体■| | |   |■(リロケータブル)■| | |   |■■■■■■■| | | 41FF|―――――――| | |   | コンパイラ・ワーク | | | 49FF|―――――――| | |   | 実行時のワーク | | | 4CFF|―――――――| | |   |■ランタイム・ルーチン■| | | 4FFF|―――――――| | |   |       | | |   | オブジェクト  | | |   | (リロケータブル) | | |   |       | | |   ・―――――――・ | ・―――――――――――――・ |>ファイル版への変更(FM−8) ファイル版というのはソース・プログラムを 外部ファイル(カセットやディスクなどの、記録媒体)に入れて、 そこから1行ずつ読み込んでコンパイルしようというものです。 こうすると図1のメモリ・マップにある ソース・プログラムのエリアは必要なくなります。 まず、リスト3のファイル版メイン・プログラムを打ち込んで セーブしてください。 次にリスト1のコンパイラをリスト4に従って変更し、 次のようにしてセーブしてください。 ・―――――――――――――――――・ | SAVEM"KF",&H2000,&H41FF,&H2000  | ・―――――――――――――――――・ 次にリスト2のランタイム・ルーチンをリスト5に従って変更し、 次のようにしてセーブしてください。 ・―――――――――――――――――・ | SAVEM"RTF",&H4D00,&H4FFF,&H4D00 | ・―――――――――――――――――・ カセットを使っている方は、 ファイル版メイン・プログラム、コンパイラ、ランタイム・ルーチンの順で 1本のカセットを作れば後々便利でしょう。 さて、実際にコンパイルする手順ですが、 始めにコンパイラもランタイム・ルーチンも メモリ上にロードしていない状態でソース・プログラムを打ち込みます。 入力が終わったら、カセットやディスクなどに ASCII形式でセーブします。 実際には次のようにします。 ・―――――――――――――――――・ | SAVE"ファイル名",A        | ・―――――――――――――――――・ 次に、ファイル版メイン・プログラムをRUNします。 最初に“HOT OR COLD?”と聞いてくるので、 コンパイラとランタイム・ルーチンが既にロードされていれば “H”を入力します(大文字で入れることを忘れずに)。 “H”以外を入力すると 外部ファイルからコンパイラとランタイム・ルーチンをロードします。 次に、“FILE NAME?”と聞いてくるので、 先に ASCII形式でセーブしたディスクまたはカセットを準備し、 そのファイル名(必要があればデバイス名も付けて)を入力します。 この後、ソース・プログラムを読み込みながらコンパイルを始めます。 |>裏RAMを使う方法(FM−7) FM−7はソフトでBASIC ROMと 裏RAMを切り換えることができるので、 この裏RAMヘコンパイラを移動して、 大きなソース・プログラムをコンパイルすることができます (コンパイラはリロケータブルです)。 まず、リスト6、7のようなリロケータとエグゼキュータを作り、 コンパイルします。 リロケータはコンパイラを裏RAMへ移動するためのもので、 エグゼキュータは移動されたコンパイラを起動するためのものです。 コンパイルは次の手順で行なってください。 (1)リロケータを使って、コンパイラを裏RAMへ移動します。   アドレスの入力は必ず“$”をつけてください。   ・―――――――――――――――――・   | 例 START ADDR=$2000       |   |   END ADDR=$41FF       |   |   DEST ADDR=$C000       |   ・―――――――――――――――――・   特に、差し支えなければ上記のように入力してください。 (2)ソース・プログラムをロードします。 (3)エグゼキュータを使って、コンパイラを実行させます。   ・―――――――――――――――――・   | 例 START ADDR=$C000       |   ・―――――――――――――――――・ |>その他の方法 ファイル版への変更や裏RAMを使う方法については 理解できたでしょうか。 どちらもディスクを使っている人はあまり不便を感じないでしょうが、 カセットの場合は多少、面倒です。 次にその欠点を挙げてみましょう。 (1)ファイル版でコンパイル中にエラーが出ると、   ソース・プログラムをロードして手直ししてセーブした後、   メイン・プログラムとコンパイラを再びロードしなければならない。 (2)リロケータやエグゼキュータを使うと   コンパイル中にエラーが出た場合、   再度ロードし実行しなければならない。   もちろん、この2つのプログラムが他の問題のない番地   (たとえば$7C00番地や$4000番地など)にあればいいのですが…。   そこで、FM−8でも裏RAMを使う方法と   マシン語で記述したリロケータ、エグゼキュータを紹介しましょう。   リスト8はFM−7/8両用のリロケータとエグゼキュータです。   プログラムはリロケータブルですが、   特に問題なければリストどおりのアドレスでかまわないでしょう。   先頭のアドレスをEXECで実行すれば   Kコンパイラを裏RAMへリロケートします。   FM−8では“ディップ・スイッチ9 OFF”と   “ディップ・スイッチ9 ON” とメッセージが表示されるので、   指示に従ってください。   次にソース・プログラムを入れて、   コンパイルするときはEXEC先頭アドレス+D0   (ここではEXEC&H7CD0)としてください。   FM−7ではメッセージは表示されません。   これはディップ・スイッチを切り換える必要がないためです。   途中で暴走してリセットしたとしても   裏RAMはクリアされないので、   Kコンパイラを再びロードする必要はありません。   本書のファイル版でしかコンパイルできないという   プログラムは大部分、   このプログラムでコンパイルできるはずです。   なお、このプログラムを使うときは一応、   CLEAR10,&H41FFとしてから、   ソース・プログラムを入れてください。 ■Kコンパイラのプログラムを入力するときの注意 Kコンパイラでは コンパイル時に完全な構文チェックをしていません。 ですから、変数などを使うときは プログラマーの責任で管理する必要があります。 特に入力時のミスで多いのは “I”と“1”と“O”と“0”といった基本的なものです。 特に変数名でこういったミスを犯すと、 Kコンパイラではまったくエラーを出しません。 しかも、そのまま暴走してしまうこともあります。 Kコンパイラのプログラムを入力するときは特に次の点に注意してください。 (1)変数名は特に注意して入力すること。 (2)サブルーチンや関数呼び出しのパラメータは、   個数が足りなかったりしてもまったくエラーを出さないので   気を付けること。 (3)CODE文に続く数値は、間違えると確実に暴走するので   正確に入力すること。 ・―――――――――――――――――・ |      最 後 に      | ・―――――――――――――――――・ 説明が不充分でわかりにくかった点もあると思いますが、 本書と『FM−8活用研究』を併読していただければ、 Kコンパイラがかなり応用の利くコンパイラであることが わかっていただけるでしょう。     表1 拡張Kコンパイラ・エントリ・マップ ・―――――――――――――――――――――――――――――――――――――・ |アドレス| 内容 |         機       能         | |――――+――――+―――――――――――――――――――――――――――| |2001 | 4C |コンパイル時作業エリア・アドレスの上位2バイト          | |――――+――――+―――――――――――――――――――――――――――| |20C0 | 4C |実行時変数エリア・アドレスの上位2バイト           | |――――+――――+―――――――――――――――――――――――――――| |20CE | 4C |実行時Uスタック・エリア最終アドレスの上位2バイト         | |――――+――――+―――――――――――――――――――――――――――| |2073,4 | 4300 |プログラム 1行入力バッファ・アドレス             | |――――+――――+―――――――――――――――――――――――――――| |207D,E | 5000 |オブジェクト作成アドレス                   | |――――+――――+―――――――――――――――――――――――――――| |2082,3 | 4400 |ラベル・テーブル・アドレス                   | |――――+――――+―――――――――――――――――――――――――――| |208E,F | 4AFE |コンパイル時スタック・エリアの最終アドレス              | |――――+――――+―――――――――――――――――――――――――――| |209A,B | 4B00 |作業用テーブル・アドレス                  | |――――+――――+―――――――――――――――――――――――――――| |4186 |サブルーチン |コンパイラ1行入力ルーチン                  | |――――+――――+―――――――――――――――――――――――――――| |41E2 | 〃 |コンパイラOPENルーチン                    | |――――+――――+―――――――――――――――――――――――――――| |41EE | 〃 |コンパイラCLOSEルーチン                    | |――――+――――+―――――――――――――――――――――――――――| |4F80 | 〃 |ランタイム 1文字入力ルーチン。エコーバック なし。         | |――――+――――+―――――――――――――――――――――――――――| |4F9B | 〃 |ランタイム 1文字出力ルーチン。                | |――――+――――+―――――――――――――――――――――――――――| |4FB5 | 〃 |ランタイム 1行入力ルーチン。エコーバック あり。          | | |    |バック・スペースあり。                   | ・―――――――――――――――――――――――――――――――――――――・     表2 エラーメッセージ ・―――――――――――――――――――――――――――――――――――――・ |DOUBLE DEF ERROR |ラベルの二重定義がある。              | |――――――――――+――――――――――――――――――――――――――| |ELSE WITHOUT IF |IF文がないのに、ELSEがきた。            | |――――――――――+――――――――――――――――――――――――――| |FI WITHOUT IF |IF文がないのに、FIがきた。             | |――――――――――+――――――――――――――――――――――――――| |ILLEGAL ARGUMENT |続、関数のパラメータに誤りがある。         | |――――――――――+――――――――――――――――――――――――――| |ILLEGAL EXPRESSION |許されない式である。                | |――――――――――+――――――――――――――――――――――――――| |ILLEGAL NESTING |FOR、IF、REPEAT、WHILE句の対応がおかしい。     | |――――――――――+――――――――――――――――――――――――――| |NEXT WITHOUT FOR |FOR文 がないのに、NEXTがきた。           | |――――――――――+――――――――――――――――――――――――――| |SYNTAX ERROR |構文に誤りがある。                 | |――――――――――+――――――――――――――――――――――――――| |TOO MANY VARIABLES |変数の数が多すぎる。                | |――――――――――+――――――――――――――――――――――――――| |UNDEFINED LABELS |未定義のラベルである。               | |――――――――――+――――――――――――――――――――――――――| |UNTIL WITHOUT REPEAT|REPEAT文がないのに、UNTIL がきた。         | |――――――――――+――――――――――――――――――――――――――| |WEND WITHOUT WHILE |WHILE文 がないのに、WENDがきた。          | ・―――――――――――――――――――――――――――――――――――――・ |>定数と変数 ・―――――――――――――――――――――――――――――――――――――・ |■■■■要■素■■■■|■■■■書■式■■■|■■■■■補■足■■■■■■| |―――――――――――+――――――――――+――――――――――――――| |定数 |1文字定数  |’で囲った1文字  |●演算の中で使用する。   | |   |―――――――+――――――――――+――――――――――――――| |   |文字列定数  |”で囲った文字列  |●関数、手続き引数中で   | |   |       |          | 使用する。        | |   |       |          |●PRINT 文中で、      | |   |       |          | 文字列出力として使用する。| |   |―――――――+――――――――――+――――――――――――――| |   |10進定数   |数値        |●-32768〜 32767の数値   | |   |       |          |●負数は“-” をつける。  | |   |―――――――+――――――――――+――――――――――――――| |   |16進数    |$数値       |●$0000〜$FFFFの数値   | |―――+―――――――+――――――――――+――――――――――――――| |変数 |単純変数   |英字で始まる英数文字|●8文字まで識別する。   | |   |―――――――+――――――――――+――――――――――――――| |   |2バイト配列変数|単純変数(式)   |●単純変数の値を配列の   | |   |       |          | 先頭 アドレスとして、    | |   |       |          | 式の値*2を加えたアドレス   | |   |       |          | から始まるバイトを指す。  | |   |―――――――+――――――――――+――――――――――――――| |   |1バイト配列変数|単純変数:式)   |●単純変数の値を配列の   | |   |       |          | 先頭 アドレスとして、    | |   |       |          | 式の値を加えた アドレスの  | |   |       |          | 1バイトを指す。      | |   |―――――――+――――――――――+――――――――――――――| |   |%変数    |%数値       |●関数、手続きの引数の参照に| |   |       |          | 用いる。         | |   |       |          |●数値は引数の順番を示す。 | |   |       |          |●局所的に領域が利用される。| |―――+―――――――+――――――――――+――――――――――――――| |ラベル|       |英字で始まる英数字列|●8文字まで識別される。  | ・―――――――――――――――――――――――――――――――――――――・ |>文 ・―――――――――――――――――――――――――――――――――――――・ |■処■理■■|■■■書■■式■■■|■■■■■■■■■補■■足■■■■■■| |――――――+――――――――――+―――――――――――――――――――| |      |変数=式       |代入   -・             | |      |+,-,*,/       |四則演算 | BASIC に準ずる。    | |一般処理  |>,>=,<,<=,=,<>   |関係演算 | 演算子の優先順位は   | |      |AND,OR       |論理演算 -・ 乗除、加減関係、論理  | |      |          |●文の区切は“;”を使う。      | |――――――+――――――――――+―――――――――――――――――――| |      |IF 条件 THEN 処理1 |●条件は式または文で記す。      | |      |    ELSE 処理2 |●条件が満足されたとき、       | |      |FI         | THENに対する処理を行ない、     | |      |          | 条件が満足されないとき、      | | 判断   |          | ELSEに対する処理を行なう。     | |      |          |●ELSE部分は省略してよい。      | |      |          |●IF文の終了は、FI文によって示す。  | |――――――+――――――――――+―――――――――――――――――――| |      |FOR 制御変数=初期値 |●BASIC に準ずる。          | |      |    TO 終了値 |                   | |for ループ  |   STEP 増分値 |●初期値>終了値のときは、      | |      |  処理      | 処理を実行しない。         | |      |NEXT        |●STEPを省略すると1とみなす。    | |      |          |●STEPは正または負の定数。      | |――――――+――――――――――+―――――――――――――――――――| |      |WHILE 条件     |●条件は式または文で記す。      | |whileループ  |   処理     |●条件が満足されている間、      | |      |WEND        | 処理を実行する。          | |――――――+――――――――――+―――――――――――――――――――| |repeat〜  |REPEAT       |●条件は式または文で記す。      | |untilループ  |   処理     |●条件が満足されるまで、       | |      |UNTIL 条件     | 処理を実行する。          | |――――――+――――――――――+―――――――――――――――――――| |サブルーチン   |GOSUB ラベル名    |●ラベル名の サブルーチンを呼び出す。    | |――――――+――――――――――+―――――――――――――――――――| |      |ラベル名〔引数列〕  |●ラベル名の関数、手続きを呼び出す。  | |      |          |●引数は”,”で区切って指定する。  | |関数、手続き|          |●関数、手続き側で引数を       | |      |          | 参照するときは、%変数を用いる。  | |      |          |●関数として使用すると、       | |      |          | 関数側で最後に実行した値を持ち帰る。| ・―――――――――――――――――――――――――――――――――――――・ |>文 ・―――――――――――――――――――――――――――――――――――――・ |処■理|■■■■書■式■■■■|■■■■■■■■■補■足■■■■■■■■■| |―――+―――――――――――+―――――――――――――――――――――| |復帰 |RETURN        |●サブルーチン 、関数、手続きから戻る。    | |―――+―――――――――――+―――――――――――――――――――――| |飛び先|GOTO ラベル名      |●ラベル制御を移す。            | |―――+―――――――――――+―――――――――――――――――――――| |コメント |(* コメント *)      |●"(*"と"*)"でくくった部分は実行しない。 | |―――+―――――――――――+―――――――――――――――――――――| |   |PRINT 出力要素並び  |●出力要素は ","で区切って指定する。   | |   |           |●出力要棄には、変数、式、文字列定数、  | |   |           | フォーマット関数、改行子が書ける。      | |出力 |           |●改行させるには "/"を使用する。     | |   |           |●"#桁数,変数または式"で、        | |   |           | 桁数分右づめで10進数にして表示する。  | |   |           |●フォーマット関数( $がついている)が使える。 | |―――+―――――――――――+―――――――――――――――――――――| |   |変数=INPUT      |●10進数、16進数(数値の前に "$"をつける)| |   |           | を値とする。              | |   |―――――――――――+―――――――――――――――――――――| |入力 |変数=GET       |●押されたキーのASCIIコード を値とする。 | |   |           |●キーが押されていないときは、0(ゼロ)を| |   |           | 値とする。               | |―――+―――――――――――+―――――――――――――――――――――| |終了 |END          |●実行を終了する。            | |―――+―――――――――――+―――――――――――――――――――――| |   |CONST 名前=定数,…  |●定数に名前をつける。          | |   |―――――――――――+―――――――――――――――――――――| |宣言 |DEF ラベル=アドレス,…   |●アドレス にラベルをつける。         | |   |           |●このラベル名に対し、関数、手続き呼び出しが| |   |           | できる。                | |―――+―――――――――――+―――――――――――――――――――――| |   |POKE アドレス,式     |●アドレス に値(1バイト)を書く。      | |   |―――――――――――+―――――――――――――――――――――| |   |CPOKE 変数名、式の例 |●変数のアドレス から、式の列を書き込む。  | |   |           |●変数の値は、書き込んだ分増加される。  | |   |           |●式は ","で区切って書く。        | |   |           | 通常は1バイトとして書くが、       | |   |           | 式の後に#をつけると2バイトとして書く。 | |   |―――――――――――+―――――――――――――――――――――| |その他|CODE 値の例      |●値の列を プログラム中に展開する。     | |   |           |●値は ","で区切って書く、        | |   |           | 通常は1バイトとして書くが、       | |   |           | 値の後に#をつけると2バイトとして書く。 | |   |―――――――――――+―――――――――――――――――――――| |   |CALL アドレス      |●アドレスを直接サブルーチン・コールする。      | ・―――――――――――――――――――――――――――――――――――――・ |>関数 ・―――――――――――――――――――――――――――――――――――――・ |■処■理■|■■■書■式■■■|■■■■■■■■■補■足■■■■■■■■■| |―――――+―――――――――+―――――――――――――――――――――| |絶対値  |ABS(式)     |●式の絶対値を値とする。         | |―――――+―――――――――+―――――――――――――――――――――| |アドレス 値 |ADR(単純変数名) |●変数のアドレス を値とする。        | |―――――+―――――――――+―――――――――――――――――――――| |上位バイト |HIGH(式)    |●式の上位バイトを値とする。        | |―――――+―――――――――+―――――――――――――――――――――| |下位バイト |LOW(式)     |●式の下位バイトを値とする。        | |―――――+―――――――――+―――――――――――――――――――――| |論理否定 |NOT(式)     |●式の論理否定を値とする。        | |     |         |●真は1、偽は0。            | |―――――+―――――――――+―――――――――――――――――――――| |アドレス 内容|PEEK(アドレス)   |●アドレス の内容(1バイト)を値とする。   | |―――――+―――――――――+―――――――――――――――――――――| |乱数   |RND(式)     |●0〜式−1の一様乱数を発生する。    | |―――――+―――――――――+―――――――――――――――――――――| |負数拡張 |SEX(式)     |●1バイトの負数を2バイトの負数とする。   | |―――――+―――――――――+―――――――――――――――――――――| |符号   |SGN(式 )    |●式の符号により、1,0,-1の値をとる。   | ・―――――――――――――――――――――――――――――――――――――・ |>フォーマット関数 ・―――――――――――――――――――――――――――――――――――――・ |■処■理■|■■■書■式■■■|■■■■■■■■■補■足■■■■■■■■■| |―――――+―――――――――+―――――――――――――――――――――| |文字   | CHR$(式)   |●式の値を文字( ASCIIコード)として出力。| |―――――+―――――――――+―――――――――――――――――――――| |16進4桁 | HEX$(式)   |●式の値を16進4桁として出力。      | |―――――+―――――――――+―――――――――――――――――――――| |16進2桁 | HEX2$(式)   |●式の値を16進2桁として出力。      | |―――――+―――――――――+―――――――――――――――――――――| |空白   | SPC$(式)   |●式の値の空白を出力。          | ・―――――――――――――――――――――――――――――――――――――・